package odie

import (
	"strings"
	"net/url"
	"errors"
	"unicode"
	"code.google.com/p/odie/langutil/pos"
)

type Pattern []string

func Parse(s string) (pat Pattern) {

//	s = strings.ToLower(s)

	pat = strings.Fields(s)
	return
}

func (p Pattern) String() string {
	return strings.Join(p, " ")
}

func skip(r rune) bool {
	return r != '\'' && (unicode.IsPunct(r) || unicode.IsSpace(r))
}

func (p Pattern) Match(q string, vals url.Values) (score int, err error) {

	// special case: empty pattern matches everything
	if len(p) == 0 || p.String() == "" {
		return 0, nil
	}

	toks := strings.FieldsFunc(q, skip)

	if len(toks) < len(p) {
		err = errors.New("length mismatch")
		return
	}

	j := 0
	for i, pi := range p {

		if j >= len(toks) {
			err = errors.New("length mismatch")
			return
		} else if strings.HasPrefix(pi, "$") {

			stop := j

			if i >= len(p) - 1 {
				// if this is the last variable, then just match everything to the end of q
				stop = len(toks) - 1
			} else if strings.HasPrefix(p[i+1], "$") {
				// if there is another variable immediately after this one, then only match one token
				stop = j
			} else {
				// match greedily
				next := strings.ToLower(p[i+1])
				for stop < len(toks) {
					if strings.ToLower(toks[stop]) == next {
						break
					}
					stop++
				}

				stop--
			}
			
			val := strings.Join(toks[j:stop+1], " ")

			if strings.HasSuffix(pi, ":N") && !pos.Is(val, pos.Noun) ||
				strings.HasSuffix(pi, ":N") && !pos.Is(val, pos.Noun) ||
				strings.HasSuffix(pi, ":V") && !pos.Is(val, pos.Verb) ||
				strings.HasSuffix(pi, ":A") && !pos.Is(val, pos.Adjective) ||
				strings.HasSuffix(pi, ":v") && !pos.Is(val, pos.Adverb) {

				err = errors.New("part of speech mismatch: " + val + " != " + pi)
				return	
			}

			id := pi

			if strings.Contains(id, ":") {
				id = strings.TrimRight(id, "NVAv")
			}

			id = strings.Trim(id, "$:")

			if vals != nil {
				vals.Add(id, val)
			}

			j = stop + 1

		} else if strings.ToLower(toks[j]) != strings.ToLower(pi) {
			err = errors.New("token mismatch: " + toks[j] + " != " + pi)
			return
		} else {
			// exact token match
			score++
			j++
		}
	}

	return
}

var ignore = map[string]bool{"a":true, "the":true, "an":true}

func (p Pattern) Keywords() (ks map[string]string) {

	ks = make(map[string]string)

	for _, tok := range p {

		tok = strings.ToLower(tok)

		if ignore[tok] {
			continue
		}

		if strings.HasPrefix(tok, "$") || tok == "*" {
			continue
		}

		ks[tok]=tok
	}

	return
}
